import os
import shlex
import sys
import yaml
import shutil
import json
import time
import getpass
import subprocess as sp
from enum import Enum
from utils.eii_util import get_eiigui_root_path, copy_files_under_dir

class EiiDevHelper:

    def __init__(self, eii_home="", user_pwd="", project_list=None):
        self._eii_home = eii_home
        self._eii_build_path = os.path.join(eii_home, "build")
        eii_dep_tool_root = get_eiigui_root_path()
        self.eii_helper_tp_build = os.path.join(eii_dep_tool_root, "eii_helper", "templates", "build")
        self.usb_deploy_script_path = os.path.join(eii_dep_tool_root, "eii_helper", "templates", "scripts", "usb_deploy")
        self._user_pwd = user_pwd
        self._user_name = getpass.getuser()
        self._project_list = project_list
        self.cur_deploy_dir = ""
        self.cur_comp_file = ""
        self.remote_deploy_dir = ""

        eii_version = self._get_eii_version()
        self.EII_VERSION = eii_version if eii_version else "2.6"
        self.REGISTRY_PORT = 5050
        self.EII_WORKSPACE_FLAG = "eii_helper/workspace"
        self.EII_CONFIG_FILE_NAME = "config.json"
        self.EIIGUI_COMPONENT_FILE_NAME = "project_components.yml"

        # Debug
        print("project list:", self._project_list)

    def remote_deploy(self, remoteip, username, password, hostip):
        # Prepare deploy files
        # Will call eii scripts to create config.json and docker-compose.yml
        self._prepare_deploy_space()
        self._prepare_shared_modules_config()
        self._prepare_eii_services()
        self._prepare_docker_compose_file()
        self._prepare_config_json_file()

        # Deploy tasks
        total_tasks = ""

        # Task of starting docker private registry on host machine
        task = self._start_registry_server_task()
        total_tasks += " ( " + task + " ) "

        # Task of pushing docker images to local registry
        task = self._push_images_to_registry_task()
        total_tasks += " && " + " ( " + task + " ) "
        
        print("Transfer pkg to remote")
        # Task of transmitting packages to target machine
        task = self._transfer_remote_packages_task(remoteip, username, password)
        total_tasks += " && " + " ( " + task + " ) "
        
        # Task of checking whether the private registry can access by target machine
        task = self._check_private_registry_task(hostip, remoteip, username, password)
        total_tasks += " && " + " ( " + task + " ) "

        # Task of pulling docker images from private registry
        task = self._pull_images_from_registry_task(hostip, remoteip, username, password)
        total_tasks += " && " + " ( " + task + " ) "

        # Task of running services
        task = self._run_remote_services_task(remoteip, username, password)
        total_tasks += " && " + " ( " + task + " ) "

        print("Deploy command start to run")
        proc = sp.Popen(total_tasks, shell=True, stdout=sp.PIPE, stderr=sp.STDOUT)
        return proc

    def local_deploy(self):
        # Prepare deploy files
        # Will call eii scripts to create config.json and docker-compose.yml
        self._prepare_deploy_space()
        self._prepare_shared_modules_config()
        self._prepare_eii_services()

        # Run up services locally
        proc = self.start_eii_stack()
        return proc

    def generate_usb_package(self, save_path):
        # Prepare deploy files
        # Will call eii scripts to create config.json and docker-compose.yml
        self._prepare_deploy_space()
        self._prepare_shared_modules_config()
        self._prepare_eii_services()
        self._prepare_docker_compose_file()
        self._prepare_config_json_file()

        # Generate offline deploy package
        task = self._generate_remote_packages_task(save_path)
        proc = sp.Popen(task, shell=True, stdout=sp.PIPE, stderr=sp.STDOUT)
        usb_provi_path = os.path.join(save_path, "build/provision")
        changereq_cmd = "sed -i 's/pyyaml==.*/pyyaml==5.4.1/g' {}/cert_requirements.txt".format(usb_provi_path)
        os.system(changereq_cmd)
        return proc

    def start_eii_stack(self):
        empty_line_cmd = self._echo_green_text_fmt.format(" ")
        post_text_cmd = self._echo_green_text_fmt.format("Deploy services locally successfully!")
        post_text_cmd_1 = self._echo_green_text_fmt.format("Check result on: localhost:5001")
        wait_cmd = "wait && sleep 1"

        total_cmds = self._join_cmds([
            self._eii_cmd_do_provision(),
            self._eii_cmd_start_containers(),
            empty_line_cmd, post_text_cmd, post_text_cmd_1, wait_cmd
        ])
        proc = sp.Popen(total_cmds, shell=True, stdout=sp.PIPE, stderr=sp.STDOUT)
        return proc

    def _eii_cmd_do_provision(self):
        provision_path = os.path.join(self._eii_build_path, "provision")
        cmd = "cd {} && echo {} | sudo -S -E ./provision.sh ../docker-compose.yml 2>&1".format(
            provision_path, self._user_pwd)
        return cmd
    
    def _eii_cmd_start_containers(self):
        cmd = "cd {} && docker-compose up -d 2>&1".format(self._eii_build_path)
        return cmd
    
    def stop_services(self):
        build_path = os.path.join(self._eii_home, "build")

        down_cmd = "cd {} && docker-compose down 2>&1".format(build_path)
        empty_line_cmd = self._echo_green_text_fmt.format("")
        post_text_cmd = self._echo_green_text_fmt.format("Stop services successfully!")
        wait_cmd = "wait && sleep 1"
        total_cmd = self._join_cmds([down_cmd, empty_line_cmd, post_text_cmd, wait_cmd])

        proc = sp.Popen(total_cmd, shell=True, stdout=sp.PIPE, stderr=sp.STDOUT)
        return proc

    def docker_logs(self, container_name):
        cmd = "docker logs -f {} 2>&1".format(container_name)
        
        proc = sp.Popen(cmd, shell=True, stdout=sp.PIPE, stderr=sp.STDOUT)
        return proc

    def list_all_push_services(self):
        try:
            compose_yml = os.path.join(self.cur_deploy_dir, "build/docker-compose.yml")
            with open(compose_yml, "r") as f:
                content = yaml.safe_load(f)
            services = list(content["services"])
            return services

        except Exception:
            return []
    
    def list_all_remote_services(self):
        push_services = self.list_all_push_services()
        extra_services = ["ia_etcd", "ia_etcd_provision"]
        for ser in extra_services:
            if ser not in push_services:
                push_services.append(ser)
        return push_services

    def _prepare_deploy_space(self):
        # Create deploy dir
        deploy_space_dir = os.path.join(self._eii_home, "eii_helper/deployspace")
        deploy_folder_name = "deploy_" + str(len(self._project_list)) + "_" + self._get_timestamp()
        self.cur_deploy_dir = os.path.join(deploy_space_dir, deploy_folder_name)
        os.makedirs(self.cur_deploy_dir, exist_ok=True)
        print("Current deploy folder: " + self.cur_deploy_dir)
        self.remote_deploy_dir = "~/eii_projects/" + deploy_folder_name

        # Copy build scripts to deploy dir
        dst_dep_build_dir = os.path.join(self.cur_deploy_dir, "build")
        shutil.copytree(self.eii_helper_tp_build, dst_dep_build_dir)

        # Copy provision scripts to deploy dir
        dst_provision_dir = os.path.join(dst_dep_build_dir, "provision")
        eii_provision_dir = os.path.join(self._eii_build_path, "provision")
        if os.path.exists(dst_provision_dir):
            shutil.rmtree(dst_provision_dir)
        shutil.copytree(eii_provision_dir, dst_provision_dir)

    def _prepare_shared_modules_config(self):
        if not self._project_list:
            print("Empty project list when prepare config")
            return
        # Multiple projects
        if len(self._project_list) > 1:
            # Combine config.json for shared modules
            web_vis_config = {}
            image_store_config = {}
            for p_name in self._project_list:
                p_path = os.path.join(self._eii_home, self.EII_WORKSPACE_FLAG, p_name)
                module_list = os.listdir(p_path)
                if module_list and len(module_list) > 0:
                    if EII_MODULE.WebVis.value in module_list:
                        module_config_path = os.path.join(p_path, EII_MODULE.WebVis.value, self.EII_CONFIG_FILE_NAME)
                        with open(module_config_path, "r") as f:
                            content = f.read()
                        if not web_vis_config:
                            web_vis_config = json.loads(content)
                        else:
                            content_json = json.loads(content)
                            its_sub = content_json["interfaces"]["Subscribers"]
                            its_labels = content_json["config"]["labels"]
                            for item in its_sub:
                                web_vis_config["interfaces"]["Subscribers"].append(item)
                            web_vis_config["config"]["labels"].update(its_labels)
                    elif EII_MODULE.ImageStore.value in module_list:
                        module_config_path = os.path.join(p_path, EII_MODULE.ImageStore.value, self.EII_CONFIG_FILE_NAME)
                        with open(module_config_path, "r") as f:
                            content = f.read()
                        if not image_store_config:
                            image_store_config = json.loads(content)
                        else:
                            content_json = json.loads(content)
                            its_sub = content_json["interfaces"]["Subscribers"]
                            for item in its_sub:
                                image_store_config["interfaces"]["Subscribers"].append(item)
            # Create combined config.json for shared modules
            if web_vis_config:
                web_vis_config_dir = os.path.join(self.cur_deploy_dir, EII_MODULE.WebVis.value)
                os.makedirs(web_vis_config_dir, exist_ok=True)
                config_file = os.path.join(self.cur_deploy_dir, EII_MODULE.WebVis.value, self.EII_CONFIG_FILE_NAME)
                with open(config_file, "w") as f:
                    f.write(json.dumps(web_vis_config, indent=4))
            if image_store_config:
                image_store_config_dir = os.path.join(self.cur_deploy_dir, EII_MODULE.ImageStore.value)
                os.makedirs(image_store_config_dir, exist_ok=True)
                config_file = os.path.join(self.cur_deploy_dir, EII_MODULE.ImageStore.value, self.EII_CONFIG_FILE_NAME)
                with open(config_file, "w") as f:
                    f.write(json.dumps(image_store_config, indent=4))

            # Create new project_components.yml file
            AppContexts = {}
            modules = []
            modules.append(EII_MODULE.EtcdUI.value)
            if web_vis_config:
                modules.append(EII_MODULE.WebVis.value)
            if image_store_config:
                modules.append(EII_MODULE.ImageStore.value)
            for p_name in self._project_list:
                component_file = os.path.join(self._eii_home, self.EII_WORKSPACE_FLAG, p_name, self.EIIGUI_COMPONENT_FILE_NAME)
                with open(component_file, "r") as f:
                    m_list = yaml.safe_load(f)
                    if m_list["AppContexts"]:
                        for m in m_list["AppContexts"]:
                            if self.EII_WORKSPACE_FLAG in m:
                                modules.append(m)
            AppContexts["AppContexts"] = modules
            self.cur_comp_file = os.path.join(self.cur_deploy_dir, self.EIIGUI_COMPONENT_FILE_NAME)
            with open(self.cur_comp_file, "w") as f:
                f.write(yaml.safe_dump(AppContexts))
            
            print("Multiple Mode: config file copy done")
        # Single project
        elif len(self._project_list) == 1:
            p_name = self._project_list[0]
            p_path = os.path.join(self._eii_home, self.EII_WORKSPACE_FLAG, p_name)
            p_modules = os.listdir(p_path)
            print("project:", p_name, "modules:", p_modules)
            # WebVisualizer config.json
            if EII_MODULE.WebVis.value in p_modules:
                p_web_vis_path = os.path.join(p_path, EII_MODULE.WebVis.value)
                dst_deploy_web_vis = os.path.join(self.cur_deploy_dir, EII_MODULE.WebVis.value)
                shutil.copytree(p_web_vis_path, dst_deploy_web_vis)
            # ImageStore config.json
            if EII_MODULE.ImageStore.value in p_modules:
                p_image_store_path = os.path.join(p_path, EII_MODULE.ImageStore.value)
                dst_deploy_image_store = os.path.join(self.cur_deploy_dir, EII_MODULE.ImageStore.value)
                shutil.copytree(p_image_store_path, dst_deploy_image_store)
            # project_components.yml
            p_project_comp = os.path.join(p_path, self.EIIGUI_COMPONENT_FILE_NAME)
            dst_project_comp = os.path.join(self.cur_deploy_dir, self.EIIGUI_COMPONENT_FILE_NAME)
            shutil.copy(p_project_comp, dst_project_comp)

            print("Single Mode: config file copy done")
        else:
            print("Error project list len")
            return

    def _prepare_eii_services(self):
        # Copy shared modules' config.json to original folder to cover
        shared_modules = os.listdir(self.cur_deploy_dir)
        # WebVis
        if EII_MODULE.WebVis.value in shared_modules:
            cur_web_vis_config = os.path.join(self.cur_deploy_dir, EII_MODULE.WebVis.value, self.EII_CONFIG_FILE_NAME)
            target_web_wis_config = os.path.join(self._eii_home, EII_MODULE.WebVis.value, self.EII_CONFIG_FILE_NAME)
            if os.path.exists(cur_web_vis_config):
                shutil.copy(cur_web_vis_config, target_web_wis_config)
        # ImageStore
        if EII_MODULE.ImageStore.value in shared_modules:
            cur_image_store_config = os.path.join(self.cur_deploy_dir, EII_MODULE.ImageStore.value, self.EII_CONFIG_FILE_NAME)
            target_image_store_config = os.path.join(self._eii_home, EII_MODULE.ImageStore.value, self.EII_CONFIG_FILE_NAME)
            if os.path.exists(cur_image_store_config):
                shutil.copy(cur_image_store_config, target_image_store_config)        
        # Start eii builder.py
        build_path = os.path.join(self._eii_home, "build")
        self.cur_comp_file = os.path.join(self.cur_deploy_dir, self.EIIGUI_COMPONENT_FILE_NAME)
        if os.path.exists(self.cur_comp_file):
            prepare_cmd = "cd {} && echo {} | sudo -S -E python3 builder.py -f {} > /dev/null 2>&1".format(
                build_path, self._user_pwd, self.cur_comp_file
            )
            sp.call(prepare_cmd, shell=True, stdout=sp.PIPE)
            print("Success to run eii builder")

    def _prepare_docker_compose_file(self):
        # Get docker-compose.yml file
        src_compose_file = os.path.join(self._eii_home, "build/docker-compose.yml")
        dst_compose_file = os.path.join(self.cur_deploy_dir, "build/docker-compose.yml")
        shutil.copy(src_compose_file, dst_compose_file)

    def _prepare_config_json_file(self):
        # Get config.json file
        src_config_file = os.path.join(self._eii_home, "build/provision/config/eii_config.json")
        dst_config_file = os.path.join(self.cur_deploy_dir, "build/provision/config/eii_config.json")
        shutil.copy(src_config_file, dst_config_file)
        
    def run_remote_services(self, hostname, username, password):
        task = self._run_remote_services_task(hostname, username, password)
        proc = sp.Popen(task, shell=True, stdout=sp.PIPE, stderr=sp.STDOUT)
        return proc

    def stop_remote_services(self, hostname, username, password):
        task = self._stop_remote_services_task(hostname, username, password)
        proc = sp.Popen(task, shell=True, stdout=sp.PIPE, stderr=sp.STDOUT)
        return proc
    
    def remote_docker_logs(self, hostname, username, password, container_name):
        task = self._remote_docker_logs_task(hostname, username, password, container_name)
        proc = sp.Popen(task, shell=True, stdout=sp.PIPE, stderr=sp.STDOUT)
        return proc
    
    def _start_registry_server_task(self):
        filter_cmd = "docker ps -q --filter \"name=eiigui-registry\""
        grep_cmd = "| grep -q . && {} ||".format("echo \"eiigui-registry is running...\"")
        pre_cmd = self._echo_green_text_fmt.format("starting eiigui-registry...")
        start_cmd = "docker run -d -p {}:5000 --restart=always --name eiigui-registry registry:2".format(
            self.REGISTRY_PORT)
        post_cmd = self._echo_blue_text_fmt.format("started eiigui-registry")
        total_cmd = filter_cmd + " " + grep_cmd + " (" + pre_cmd + " && " + start_cmd + " && " + post_cmd + ")"
        return total_cmd
    
    def _push_images_to_registry_task(self):
        services = self.list_all_remote_services()
        all_cmds = []
        for ser in services:
            pre_text = "Pushing docker image: {}...".format(ser)
            cmd_pre_text = self._echo_green_text_fmt.format(pre_text)
            if ser in ["ia_etcd", "ia_etcd_provision", "ia_etcd_ui", "ia_web_visualizer", "ia_imagestore"]:
                local_img = "openedgeinsights/" + ser
            else:
                local_img = "eiigui/" + ser
            registry_img = "localhost:{}/{}:{}".format(self.REGISTRY_PORT, ser, self.EII_VERSION)
            rename_cmd = "docker tag {}:{} {}".format(local_img, self.EII_VERSION, registry_img)
            push_cmd = "docker push {} > /dev/null 2>&1".format(registry_img)
            remove_local_cmd = "docker image remove {} > /dev/null 2>&1".format(registry_img)
            post_text = "Pushed docker image: {}.".format(ser)
            cmd_post_text = self._echo_blue_text_fmt.format(post_text)

            cmd = self._join_cmds([cmd_pre_text, rename_cmd, push_cmd, remove_local_cmd, cmd_post_text])
            all_cmds.append(cmd)

        # Multi processes for generating deployment packages
        # Prepare shell command
        task = "( {} ) & "
        total_tasks = ""
        for cmd in all_cmds:
            total_tasks += task.format(cmd)
        finished_text = "All docker images pushed to local registry: {}".format("eii-registry")
        cmd_finished_text = self._echo_blue_text_fmt.format(finished_text)
        total_tasks += "wait && {} && sleep 1".format(cmd_finished_text)

        return total_tasks

    def _check_private_registry_task(self, local_hostname, rhostname, rusername, rpassword):
        ssh_cmd = self._get_ssh_cmd(rpassword, rusername, rhostname)
        build_path = os.path.join(self.remote_deploy_dir, "build")
        script_file = os.path.join(build_path, "check_private_registry.py")
        pre_text_cmd = self._echo_green_text_fmt.format(
            "Config the target machine with eii-gui registry...")
        exec_cmd = "echo {} | sudo -S python3 {} {} {}".format(
            rpassword, script_file, local_hostname, self.REGISTRY_PORT
        )
        post_text_cmd = self._echo_blue_text_fmt.format("The eii-gui registry configured.")
        remote_cmd = ssh_cmd + " \"{}\" ".format(
            self._join_cmds([pre_text_cmd, exec_cmd, post_text_cmd])
        )

        total_tasks = remote_cmd
        
        return total_tasks
    
    def _pull_images_from_registry_task(self, local_hostname, rhostname, rusername, rpassword):
        all_cmds = []
        
        services = self.list_all_remote_services()
        for ser in services:
            pre_text = "Pulling docker image: {}...".format(ser)
            cmd_pre_text = self._echo_green_text_fmt.format(pre_text)
            
            ssh_cmd = self._get_ssh_cmd(rpassword, rusername, rhostname)
            if ser in ["ia_etcd", "ia_etcd_provision", "ia_etcd_ui", "ia_web_visualizer", "ia_imagestore"]:
                local_img = "openedgeinsights/" + ser
            else:
                local_img = "eiigui/" + ser
            registry_img = "{}:{}/{}:{}".format(
                local_hostname, self.REGISTRY_PORT, ser, self.EII_VERSION)
            pull_cmd = "docker pull {} > /dev/null 2>&1".format(registry_img)
            rename_cmd = "docker tag {} {}:{}".format(registry_img, local_img, self.EII_VERSION)
            remove_remote_cmd = "docker image remove {} > /dev/null 2>&1".format(registry_img)
            exec_cmd = "\"{}\"".format(self._join_cmds([pull_cmd, rename_cmd, remove_remote_cmd])) 
            remote_cmd = ssh_cmd + exec_cmd

            post_text = "Pulling completed: {}.".format(ser)
            cmd_post_text = self._echo_blue_text_fmt.format(post_text)

            cmd = self._join_cmds([cmd_pre_text, remote_cmd, cmd_post_text])
            all_cmds.append(cmd)
        
        # Multi processes for loading packages
        # Prepare shell command
        task = "( {} ) & "
        total_tasks = ""
        for cmd in all_cmds:
            total_tasks += task.format(cmd)
        finished_text = "All docker images pulled."
        cmd_finished_text = self._echo_blue_text_fmt.format(finished_text)
        total_tasks += "wait && {} && sleep 1".format(cmd_finished_text)
        return total_tasks
    
    def _generate_remote_packages_task(self, save_path):
        # Create save path
        os.makedirs(save_path, exist_ok=True)

        # Copy build file
        deploy_build_dir = os.path.join(self.cur_deploy_dir, "build")
        save_path_build = os.path.join(save_path, "build")
        if os.path.exists(save_path_build):
            shutil.rmtree(save_path_build)
        shutil.copytree(deploy_build_dir, save_path_build)

        # Copy usb deploy scripts
        copy_files_under_dir(self.usb_deploy_script_path, save_path)

        # Generate docker offline image
        services = self.list_all_remote_services()
        all_cmds = []
        for ser in services:
            pre_text = "Generating docker image: {}...".format(ser)
            cmd_pre_text = self._echo_green_text_fmt.format(pre_text)
            if ser in ["ia_etcd", "ia_etcd_provision", "ia_etcd_ui", "ia_web_visualizer", "ia_imagestore"]:
                new_ser = "openedgeinsights/" + ser
            else:
                new_ser = "eiigui/" + ser
            exec_cmd = "cd {} && echo {} | sudo -S docker save -o {}.tar {}:{} 2>&1 && sudo chown {}:{} {}.tar".format(
                save_path, self._user_pwd, ser, new_ser, self.EII_VERSION, self._user_name, self._user_name, ser
            )
            post_text = "Generated docker image: {}.".format(ser)
            cmd_post_text = self._echo_blue_text_fmt.format(post_text)

            cmd = self._join_cmds([cmd_pre_text, exec_cmd, cmd_post_text])
            all_cmds.append(cmd)

        # Multi processes for generating deployment packages
        # Prepare shell command
        task = "( {} ) & "
        total_tasks = ""
        for cmd in all_cmds:
            total_tasks += task.format(cmd)
        finished_text = "All docker images saved in path: {}".format(save_path)
        cmd_finished_text = self._echo_blue_text_fmt.format(finished_text)
        total_tasks += "wait && {} && sleep 1".format(cmd_finished_text)
        return total_tasks

    def _transfer_remote_packages_task(self, hostname, username, password):
        src_dep_dir = self.cur_deploy_dir
        # Create the project directory on the remote machine
        cmd_dst_dep_pkg_dir = "sshpass -p {} ssh -o StrictHostKeyChecking=no {}@{} \"mkdir -p {} \" > /dev/null 2>&1".format(
            password, username, hostname, self.remote_deploy_dir,
        )
        sp.call(cmd_dst_dep_pkg_dir, shell=True)

        # Prepare build & provision command
        pre_text = "Transferring essential project config files..."
        cmd_pre_text = self._echo_green_text_fmt.format(pre_text)
        exec_cmd = "sshpass -p {} scp -o StrictHostKeyChecking=no -r {} {}@{}:{} 2>&1".format(
            password, os.path.join(src_dep_dir, "build"), username, hostname, self.remote_deploy_dir
        )
        cp_cmd = "sshpass -p {} scp -o StrictHostKeyChecking=no -r {} {}@{}:{} 2>&1".format(
            password, os.path.join(self.usb_deploy_script_path, "*"), username, hostname, self.remote_deploy_dir
        )
        post_text = "Transfer completed, essential project config files."
        cmd_post_text = self._echo_blue_text_fmt.format(post_text)
        total_tasks = self._join_cmds([cmd_pre_text, exec_cmd, cp_cmd, cmd_post_text])

        return total_tasks

    def _run_remote_services_task(self, hostname, username, password):
        build_path = os.path.join(self.remote_deploy_dir, "build")
        provision_path = os.path.join(build_path, "provision")

        ssh_cmd = self._get_ssh_cmd(password, username, hostname)
        stop_web_vis_cmd = self._remove_container_cmd("web_visualizer")
        changereq_cmd = "sed -i 's/pyyaml==.*/pyyaml==5.4.1/g' {}/cert_requirements.txt".format(provision_path)
        provision_cmd = "cd {} && echo {} | sudo -S -E ./provision.sh ../docker-compose.yml  2>&1".format(
            provision_path, password)
        up_cmd = "cd .. && docker-compose up -d  2>&1"
        empty_line_cmd = self._echo_green_text_fmt.format("")
        post_text_cmd = self._echo_green_text_fmt.format("Run services successfully!")
        wait_cmd = "wait && sleep 1"
        joined_cmds = self._join_cmds(
            [stop_web_vis_cmd, changereq_cmd, provision_cmd, up_cmd, empty_line_cmd, post_text_cmd, wait_cmd])
        total_cmd = ssh_cmd + "\'" + joined_cmds + "\'"

        return total_cmd

    def _stop_remote_services_task(self, hostname, username, password):
        build_path = os.path.join(self.remote_deploy_dir, "build")

        ssh_cmd = self._get_ssh_cmd(password, username, hostname)
        down_cmd = "cd {} && docker-compose down 2>&1".format(build_path)
        empty_line_cmd = self._echo_green_text_fmt.format(" ")
        post_text_cmd = self._echo_green_text_fmt.format("Stop services successfully!")
        wait_cmd = "wait && sleep 1"
        joined_cmds = self._join_cmds([down_cmd, empty_line_cmd, post_text_cmd, wait_cmd])
        total_cmd = ssh_cmd + "\'" + joined_cmds + "\'"

        return total_cmd

    def _remote_docker_logs_task(self, hostname, username, password, container_name):
        ssh_cmd = self._get_ssh_cmd(password, username, hostname)
        logs_cmd = "docker logs -f {} 2>&1 &".format(container_name)
        total_cmd = ssh_cmd + "\'" + logs_cmd + "\'"

        return total_cmd

    def stop_local_services():
        cmd_list_container = ["docker", "ps", "-aq"]
        con_result = sp.check_output(cmd_list_container)
        container_list = shlex.split(con_result.decode('utf-8'))
        rm_cmd = ["docker", "rm", "-f"] + container_list
        rm_cm_str = " ".join(rm_cmd)

        empty_line_cmd = "echo ' '"
        post_text_cmd = "echo Stop services successfully!"
        wait_cmd = "wait && sleep 1"

        joined_cmds = " && ".join([rm_cm_str, empty_line_cmd, post_text_cmd, wait_cmd])

        proc = sp.Popen(joined_cmds, shell=True, stdout=sp.PIPE, stderr=sp.STDOUT)
        return proc

    def log_display(msg):
        cmd = ["echo", msg]
        proc = sp.Popen(cmd, shell=False, stdout=sp.PIPE, stderr=sp.STDOUT)
        return proc
    
    def _get_eii_version(self):
        try:
            env_file = os.path.join(self._eii_home, "build", ".env")
            with open(env_file, "r") as f:
                lines = f.readlines()
                for line in lines:
                    if "EII_VERSION" in line:
                        version = line.strip().split("=")[-1]
                        return version
        except:
            return ""

    def _get_timestamp(self):
        t = int(time.time())
        return str(t)

    @staticmethod
    def _remove_container_cmd(container_name):
        return "(docker ps -q --filter \"name={0}\" | grep -q . && docker rm -f {0} || :)".format(
            container_name) 
    
    @staticmethod
    def _get_ssh_cmd(password, username, hostname):
        ssh_cmd = "sshpass -p {} ssh -o StrictHostKeyChecking=no {}@{} ".format(
            password, username, hostname
        )
        return ssh_cmd
    
    @property
    def _echo_green_text_fmt(self):
        return " echo \"\033[32m{}\033[0m\" "

    @property
    def _echo_blue_text_fmt(self):
        return " echo \"\033[34m{}\033[0m\" "

    @staticmethod
    def _join_cmds(cmds):
        return " && ".join(cmds)


class EII_MODULE(Enum):
    WebVis = "WebVisualizer"
    ImageStore = "ImageStore"
    EtcdUI = "EtcdUI"
